/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "FuncEvaluator.h"
#include <math.h>
#include <time.h>

Evolutive::CFuncEvaluator::CFuncEvaluator(void) : CEvaluator(), m_SelectedFunc(PREDFUNC_NONE),m_Parameters(NULL)
{	
}

Evolutive::CFuncEvaluator::~CFuncEvaluator(void)
{	
}

void Evolutive::CFuncEvaluator::SetFncParameters(double *Parameters)
{
	m_Parameters=Parameters;
}

void Evolutive::CFuncEvaluator::SetFunction(PREDEF_FUNCTION Function,int NumVars)
{
	register int i;

	//! Store the function type	
	m_SelectedFunc=Function;
	
	//! Store the number of variables
	m_NumFields=NumVars;

	//! Store the fields description
	if(m_FieldsNumBits)
		delete[] m_FieldsNumBits;	
	m_FieldsNumBits=new int[m_NumFields];

	// Depending on the selected function use the different approaches
	switch(m_SelectedFunc)
	{
	case PREDFUNC_NONE:
		
		break;
	case PREDFUNC_4_PEAKS:			
	case PREDFUNC_SUM:		
	case PREDFUNC_HIFF:		
	case PREDFUNC_ISOPEAK:		
	case PREDFUNC_ISOTORUS:
	case PREDFUNC_ONEMAX:
	case PREDFUNC_PLATEAU:
	case PREDFUNC_CHECKERBOARD:
	case PREDFUNC_EQPRODUCTS:
	case PREDFUNC_6_PEAKS:
		// These problems use a binary representation (1 bit per variable).
		for(i=0;i<m_NumFields;i++)
			m_FieldsNumBits[i]=1;
		break;
	case PREDEF_SPHERE:		
	case PREDEF_RASTRIGIN:		
	case PREDEF_ROSENBROCK:		
	case PREDEF_GRIEWANGK:		
	case PREDEF_ACKLEYS:
		// These problems use a double representation (64 bits per variable).
		for(i=0;i<m_NumFields;i++)
			m_FieldsNumBits[i]=64;
		break;
	}	
}

double Evolutive::CFuncEvaluator::GetScore(Evolutive::CChromosome *C)
{	
	double Score;
		
	// Depending on the selected function use the different approaches
	switch(m_SelectedFunc)
	{
	case PREDFUNC_NONE:
		Score=0;
		break;
	case PREDFUNC_4_PEAKS:	
		Score=Fnc4Peaks(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_SUM: //! Sum of all the fields
		Score=FncSum(m_NumFields,C->GetIntStringPtr());		
		break;
	case PREDFUNC_HIFF:
		Score=FncHIFF(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_ISOPEAK:
		Score=FncIsoPeak(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_ISOTORUS:
		Score=FncIsoTorus(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDEF_SPHERE:
		Score=FncSphere(m_NumFields,C->GetDblStringPtr());
		break;
	case PREDEF_RASTRIGIN:
		Score=FncRastrigin(m_NumFields,C->GetDblStringPtr());
		break;
	case PREDEF_ROSENBROCK:
		Score=FncRosenbrock(m_NumFields,C->GetDblStringPtr());
		break;
	case PREDEF_GRIEWANGK:
		Score=FncGriewangk(m_NumFields,C->GetDblStringPtr());
		break;
	case PREDEF_ACKLEYS:
		Score=FncAckleys(m_NumFields,C->GetDblStringPtr());
		break;
	case PREDFUNC_ONEMAX:
		Score=FncOneMax(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_PLATEAU:
		Score=FncPlateau(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_CHECKERBOARD:
		Score=FncCheckerboard(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_EQPRODUCTS:
		Score=FncEqProducts(m_NumFields,C->GetIntStringPtr());
		break;
	case PREDFUNC_6_PEAKS:
		Score=Fnc6Peaks(m_NumFields,C->GetIntStringPtr());
		break;
	}

	return Score;
}

double Evolutive::CFuncEvaluator::GetOptimumValue(void)
{
	double OptimumValue;
	register int i;
	unsigned int *Values=new unsigned int[m_NumFields];
	double *RandValues=new double[m_NumFields];
	double k=5,s=-1;

	// Get the optimum value for the selected function
	switch(m_SelectedFunc)
	{
	case PREDFUNC_SUM: // Sum of all the bits
		OptimumValue=m_NumFields;
		break;
	case PREDFUNC_4_PEAKS: // Four-peaks function
		OptimumValue=static_cast<int>(m_Parameters[0])-static_cast<int>(m_Parameters[1]);
		break;
	case PREDFUNC_HIFF: // HIFF function
		OptimumValue=(log(static_cast<double>(m_NumFields))/log(2.0))*m_NumFields+m_NumFields;
		break;
	case PREDFUNC_ISOPEAK: // IsoPeak function
		OptimumValue=m_NumFields*(m_NumFields/2.0);
		break;
	case PREDFUNC_ISOTORUS:	// IsoTorus function
		OptimumValue=sqrt(static_cast<double>(m_NumFields))+(m_NumFields-1)*m_NumFields;
		break;
	case PREDEF_SPHERE:
		OptimumValue=0.0;
		break;
	case PREDEF_RASTRIGIN:
		OptimumValue=0.0;
		break;
	case PREDEF_ROSENBROCK:
		OptimumValue=0.0;
		break;
	case PREDEF_GRIEWANGK:
		OptimumValue=0.0;
		break;
	case PREDEF_ACKLEYS:
		OptimumValue=0.0;
		break;
	case PREDFUNC_ONEMAX:
		OptimumValue=m_NumFields;
		break;
	case PREDFUNC_PLATEAU:
		// K=m_Parameters[0]
		OptimumValue=m_NumFields/m_Parameters[0];
		break;
	case PREDFUNC_CHECKERBOARD:
		OptimumValue=4.0*pow(sqrt(static_cast<double>(m_NumFields))-2.0,2);
		break;
	case PREDFUNC_EQPRODUCTS:
		// K=m_Parameters[0]
		// m_Parameters[1] contains the seed for random numbers generation or -1
		RandValues=new double[m_NumFields];
		k=5;
		s=-1;

		if(m_Parameters)
		{
			k=m_Parameters[0];
			s=m_Parameters[1];
		}

		// Select the random seed
		if(s==-1)
			srand( (unsigned)time( NULL ) );
		else
			srand(s);

		// Generate the random values and set the individuals to 0 (best value)
		for(i=0;i<m_NumFields;i++)
		{
			RandValues[i]=RAND_VALUE()*k;
			Values[i]=0;
		}

		// Get the value for the optimal individual
		OptimumValue=FncEqProducts(m_NumFields,Values);

		delete[] RandValues;
		break;
	case PREDFUNC_6_PEAKS:
		// t=m_Parameters[0]
		OptimumValue=m_NumFields+(m_NumFields-(m_Parameters[0]+1));
		break;
	}

	delete[] Values;

	return OptimumValue;
}

bool Evolutive::CFuncEvaluator::IsValid(CChromosome *C)
{
	// We consider all the possible values of the variables as possible values
	return true;
}

double Evolutive::CFuncEvaluator::Fnc4Peaks(int NumFields,unsigned int *Values)
{
	double Score;
	register int i;
	int nZeros,nOnes;
	int Reward;
	int Thr=10;

	// If some parameter is given, use it as threshold
	if(m_Parameters)
		Thr=static_cast<int>(m_Parameters[0]);

	// Initialize the counter
	Score=0;

	// Count the contiguous ones at the beginning
	nOnes=0;
	for(i=0;i<NumFields;i++)
	{
		if(!Values[i])
			break;
		nOnes++;
	}

	// Count the contiguous zeros at the end
	nZeros=0;
	for(i=NumFields-1;i>=0;i--)
	{
		if(Values[i])
			break;
		nZeros++;
	}

	// Calculate the reward value
	Reward=0;
	if(nOnes>Thr && nZeros>Thr)
		Reward=NumFields;

	// Calculate the final score
	Score=max(nOnes,nZeros)+Reward;

	return Score;
}

double Evolutive::CFuncEvaluator::FncSum(int NumFields,unsigned int *Values)
{
	double Score;
	register int i;

	//! Initialize the counter
	Score=0;

	//! Sum all the values
	for(i=0;i<NumFields;i++)
		Score+=Values[i];

	return Score;
}

double Evolutive::CFuncEvaluator::FncHIFF(int NumFields,unsigned int *Values)
{
	register int i;
	double Score;
	unsigned int Sum;
	
	//! Check the base case
	if(NumFields>1)
	{
		//! Initialize the score
		Score=0.0;

		//! Sum all the bits
		Sum=0;
		for(i=0;i<NumFields;i++)
		{
			Sum+=Values[i];
		}

		//! Verify the sum
		if(Sum==0 || Sum==NumFields)
		{
			Score=NumFields;
		}

		//! Evaluate the both sides
		Score+=(FncHIFF(NumFields/2,Values)+FncHIFF(NumFields/2,&(Values[NumFields/2])));
	}
	else
	{
		Score=1;
	}
		
	return Score;
}

double Evolutive::CFuncEvaluator::FncIsoPeak(int NumFields,unsigned int *Values)
{
	register int i;
	double Score;
	double m;

	m=NumFields-1;

	//! Initialize the counter
	Score=0;

	//! IsoC2
	if(Values[0]==1 && Values[1]==1)
		Score+=m;

	//! IsoC1
	for(i=1;i<m;i++)
	{
		if(Values[i]==0 && Values[i+1]==0)
			Score+=m;
		if(Values[i]==1 && Values[i+1]==1)
			Score+=m-1;
	}
	
	return Score;
}

double Evolutive::CFuncEvaluator::FncIsoTorus(int NumFields,unsigned int *Values)
{
	double Score;
	int m,n;
	int u;
	int x,y;

	//! Initialize the counter
	Score=0;

	//! Obtain the grid size
	n=NumFields;
	m=floor(sqrt((double)n));
	
	for(y=0;y<m;y++)
	{
		for(x=0;x<m;x++)
		{
			//! IsoT1
			if(x==0 && y==0)
			{					
				u=Values[n-m]+Values[m-1]+Values[0]+Values[1]+Values[m];

				//! Add the score
				if(u==5)
					Score+=n;								
			}
			else
			{
				//! IsoT2	
				u=Values[y*m+x];
				//! Verify the upper position
				if(y>0)
					u+=Values[(y-1)*m+x];
				//! Verify the left position
				if(x>0)
					u+=Values[y*m+(x-1)];
				//! Verify the right position
				if(x<m-1)
					u+=Values[y*m+(x+1)];
				//! Verify the down position
				if(y<m-1)
					u+=Values[(y+1)*m+x];			

				//! Add the score
				if(u==0)
					Score+=m;
				if(u==5)
					Score+=m-1;				
			}
		}
	}

	return Score;
}

double Evolutive::CFuncEvaluator::FncSphere(int NumFields,double *Values)
{
	double Score;
	register int i;

	// f(x)=Sum_{i=1}^{n} (x_i)^2
	Score=0;
	for(i=0;i<NumFields;i++)
	{
		Score+=Values[i]*Values[i];
	}

	return Score;
}

double Evolutive::CFuncEvaluator::FncRastrigin(int NumFields,double *Values)
{
	double Score;
	register int i;

	// f(x)=Sum_{i=1}^{n} [(x_i)^2-10cos(2*pi*x_i)+10]
	Score=0;
	for(i=0;i<NumFields;i++)
	{
		Score+=(Values[i]*Values[i])-(10*cos(2.0*PI*Values[i])+10.0);
	}

	return Score;
}

double Evolutive::CFuncEvaluator::FncRosenbrock(int NumFields,double *Values)
{
	double Score;
	double Aux;
	register int i;

	// f(x)=Sum_{i=1}^{n-1} [(x_i)^2-(x_{i+1})^2+(x_i - 1)^2]
	Score=0;
	for(i=0;i<NumFields-1;i++)
	{
		Aux=(Values[i]*Values[i])-Values[i+1];
		Score+=(Aux*Aux)+(Values[i]-1)*(Values[i]-1);
	}

	return Score;
}

double Evolutive::CFuncEvaluator::FncGriewangk(int NumFields,double *Values)
{
	double Score;
	double Aux;
	register int i;

	// f(x)=Sum_{i=1}^n [(x_i)^2/4000] - Prod_{i=1}^n [cos(x_i/i^0.5)] + 1
	Score=0;
	Aux=1;
	for(i=0;i<NumFields;i++)
	{
		Score+=(Values[i]*Values[i])/4000.0;
		Aux*=cos(Values[i]/sqrt((double)i));
	}
	Score+=Aux+1;

	return Score;
}

double Evolutive::CFuncEvaluator::FncAckleys(int NumFields,double *Values)
{
	double Score;
	double Aux1,Aux2;
	double TwoPi=2.0*PI;
	register int i;

	// f(x)=-20exp(-0.2*sqrt(1/30 * Sum_{i=1}^n (x_i)^2) - exp(1/30 * Sum_{i=1}^n cos(2*pi*(x_i))) + 20 + e
	Aux1=0;
	Aux2=0;
	for(i=0;i<NumFields;i++)
	{		
		Aux1+=Values[i]*Values[i];
		Aux2+=cos(TwoPi*Values[i]);
	}
	Score=-20.0*exp(-0.2*sqrt((1.0/30.0)*Aux1)) - exp((1.0/30.0)*Aux2) + 20.0 + exp(1.0);

	return Score;
}

double Evolutive::CFuncEvaluator::FncOneMax(int NumFields,unsigned int *Values)
{
	double Score;
	register int i;

	// Initialize the counter
	Score=0;

	// Sum all the values
	for(i=0;i<NumFields;i++)
		Score+=Values[i];

	return Score;
}

double Evolutive::CFuncEvaluator::FncPlateau(int NumFields,unsigned int *Values)
{
	double Score;
	int k=4;
	register int i,j;
	int Acc;
	unsigned int *pValues=NULL;

	// Store the parameters
	if(m_Parameters)
		k=m_Parameters[0];

	double m=NumFields/k;

	// Initialize the counter
	Score=0;
	for(i=0;i<m;i++)
	{
		Acc=1;
		pValues=&(Values[i*k]);
		for(j=0;j<k;j++)
		{
			if(!pValues[j])
			{
				Acc=0;
				continue;
			}
		}
		Score+=Acc;
	}
	return Score;
}

double Evolutive::CFuncEvaluator::FncCheckerboard(int NumFields,unsigned int *Values)
{
	double Score,Aux;
	int s=floor(sqrt(static_cast<double>(NumFields)));
	register int i,j;

	//! Initialize the counter
	Score=0;
	Aux=0;
	for(i=1;i<s-1;i++)
	{
		for(j=1;j<s-1;j++)
		{
			if(Values[i*s+j]==Values[(i-1)*s+j])
				Aux++;
			if(Values[i*s+j]==Values[(i+1)*s+j])
				Aux++;
			if(Values[i*s+j]==Values[i*s+j-1])
				Aux++;
			if(Values[i*s+j]==Values[i*s+j+1])
				Aux++;
		}
	}
	Score=4.0*pow(s-2.0,2)-Aux;
	return Score;
}

double Evolutive::CFuncEvaluator::FncEqProducts(int NumFields,unsigned int *Values)
{
	double Score;
	double k=5,s=-1;
	double *RandValues=new double[NumFields];
	register int i;

	// Get the parameters
	if(m_Parameters)
	{
		k=static_cast<double>(m_Parameters[0]);
		s=static_cast<double>(m_Parameters[1]);
	}

	// Set the random seed
	if(s==-1)
		srand( (unsigned)time( NULL ) );
	else
		srand(s);

	// Generate the random values
	for(i=0;i<NumFields;i++)
		RandValues[i]=RAND_VALUE()*k;

	// Initialize the counter
	Score=1;

	for(i=0;i<NumFields;i++)
	{
		if(!Values[i])
			Score*=1-RandValues[i];
		else
            Score*=RandValues[i]-1;
	}

	delete[] RandValues;
	return fabs(Score);
}

double Evolutive::CFuncEvaluator::Fnc6Peaks(int NumFields,unsigned int *Values)
{
	double Score;
	register int i;
	int LnZeros,LnOnes;
	int RnZeros,RnOnes;
	int Reward;
	int Thr=10;

	// If some parameter is given, use it as threshold
	if(m_Parameters)
		Thr=static_cast<int>(m_Parameters[0]);

	// Initialize the counter
	Score=0;
	
	// Count the contiguous ones at the beginning
	LnOnes=0;
	for(i=0;i<NumFields;i++)
	{
		if(!Values[i])
			break;
		LnOnes++;
	}
	// Count the contiguous zeros at the beginning
	LnZeros=0;
	for(i=0;i<NumFields;i++)
	{
		if(Values[i])
			break;
		LnZeros++;
	}

	// Count the contiguous zeros at the end
	RnZeros=0;
	for(i=NumFields-1;i>=0;i--)
	{
		if(Values[i])
			break;
		RnZeros++;
	}
	// Count the contiguous ones at the end
	RnOnes=0;
	for(i=NumFields-1;i>=0;i--)
	{
		if(!Values[i])
			break;
		RnOnes++;
	}

	// Calculate the reward value
	Reward=0;
	if((LnOnes>Thr && RnZeros>Thr) || (RnOnes>Thr && LnZeros>Thr))
		Reward=NumFields;

	// Calculate the final score
	Score=max(max(max(LnOnes,LnZeros),RnOnes),RnZeros)+Reward;

	return Score;
}

char* Evolutive::CFuncEvaluator::GetProblemName(char *Buffer)
{
	char *Name;

	// Fill the buffer with the problem name
	switch(m_SelectedFunc)
	{
	case PREDFUNC_SUM:
		Name="Sum of bits";
		break;
	case PREDFUNC_4_PEAKS:
		Name="Four peaks";
		break;
	case PREDFUNC_HIFF: 
		Name="HIFF";
		break;
	case PREDFUNC_ISOPEAK:
		Name="IsoPeak";
		break;
	case PREDFUNC_ISOTORUS:
		Name="IsoTorus";
		break;
	case PREDEF_SPHERE:
		Name="Sphere";
		break;
	case PREDEF_RASTRIGIN:
		Name="Sum of bits";
		break;
	case PREDEF_ROSENBROCK:
		Name="Rosenbrock";
		break;
	case PREDEF_GRIEWANGK:
		Name="Griewangk";
		break;
	case PREDEF_ACKLEYS:
		Name="Ackleys";
		break;
	case PREDFUNC_ONEMAX:
		Name="OneMax";
		break;
	case PREDFUNC_PLATEAU:
		Name="Plateau";
		break;
	case PREDFUNC_CHECKERBOARD:
		Name="Checkboard";
		break;
	case PREDFUNC_EQPRODUCTS:
		Name="Equal products";
		break;
	case PREDFUNC_6_PEAKS:
		Name="Six Peaks";
		break;
	}

	sprintf(Buffer,"%s",Name);

	return Buffer;
}